#ifndef __PDTTRAVERSALIFC_H_
#define __PDTTRAVERSALIFC_H_

#define TRAVERSAL_CLASS_METHODS(D) \
        virtual RetType pre(D & d) { return Continue;} \
        virtual RetType post(D & d) {return Continue;} \
        virtual RetType beforeChild(D & d, unsigned int i) {return Continue;} \
        virtual RetType afterChild(D & d, unsigned int i) {return Continue;}

class PDTAdd;
class PDTAnd;
class PDTAssert;
class PDTAssign;
class PDTPiAssign;
class PDTBehavior;
class PDTBehaviors;
class PDTBoolConst;
class PDTBoolVar;
class PDTConstSequence;
class PDTDiv;
class PDTEqualBool;
class PDTEqualNum;
class PDTExist;
class PDTForall;
class PDTFunctionCall;
class PDTFunctionDec;
class PDTGreaterThan;
class PDTGreaterThanEqual;
class PDTIfElse;
class PDTImplies;
class PDTIntRange;
class PDTInvariant;
class PDTLessThan;
class PDTLessThanEqual;
class PDTListOfStatements;
class PDTMod;
class PDTMult;
class PDTNeg;
class PDTNilStatement;
class PDTNot;
class PDTNotEqualBool;
class PDTNotEqualNum;
class PDTNumConst;
class PDTNumConstDbl;
class PDTNumConstInt;
class PDTNumVar;
class PDTOr;
class PDTPair;
class PDTPostCondition;
class PDTPreCondition;
class PDTProp;
class PDTRelation;
class PDTSequence;
class PDTSequenceAccessBool;
class PDTSequenceAccessNum;
class PDTSequenceAssign;
class PDTSequenceVar;
class PDTSet;
class PDTSetVar;
class PDTSubt;
class PDTWhile;
class PDTXor;
class PDTUnresolvedExp;
class PDTType;
class PDTUnaryNumExpr;


class Traversal { 
    public:
        virtual ~Traversal(){};
    public:

        typedef enum {
            Stop, Continue, Skip,
            LastRetType
        } RetType;


TRAVERSAL_CLASS_METHODS(PDTAdd)
TRAVERSAL_CLASS_METHODS(PDTAnd)
TRAVERSAL_CLASS_METHODS(PDTAssert)
TRAVERSAL_CLASS_METHODS(PDTAssign)
TRAVERSAL_CLASS_METHODS(PDTPiAssign)
TRAVERSAL_CLASS_METHODS(PDTBehavior)
TRAVERSAL_CLASS_METHODS(PDTBehaviors)
TRAVERSAL_CLASS_METHODS(PDTBoolConst)
TRAVERSAL_CLASS_METHODS(PDTBoolVar)
TRAVERSAL_CLASS_METHODS(PDTConstSequence)
TRAVERSAL_CLASS_METHODS(PDTDiv)
TRAVERSAL_CLASS_METHODS(PDTEqualBool)
TRAVERSAL_CLASS_METHODS(PDTEqualNum)
TRAVERSAL_CLASS_METHODS(PDTExist)
TRAVERSAL_CLASS_METHODS(PDTForall)
TRAVERSAL_CLASS_METHODS(PDTFunctionCall)
TRAVERSAL_CLASS_METHODS(PDTFunctionDec)
TRAVERSAL_CLASS_METHODS(PDTGreaterThan)
TRAVERSAL_CLASS_METHODS(PDTGreaterThanEqual)
TRAVERSAL_CLASS_METHODS(PDTIfElse)
TRAVERSAL_CLASS_METHODS(PDTImplies)
TRAVERSAL_CLASS_METHODS(PDTIntRange)
TRAVERSAL_CLASS_METHODS(PDTInvariant)
TRAVERSAL_CLASS_METHODS(PDTLessThan)
TRAVERSAL_CLASS_METHODS(PDTLessThanEqual)
TRAVERSAL_CLASS_METHODS(PDTListOfStatements)
TRAVERSAL_CLASS_METHODS(PDTMod)
TRAVERSAL_CLASS_METHODS(PDTMult)
TRAVERSAL_CLASS_METHODS(PDTNeg)
TRAVERSAL_CLASS_METHODS(PDTNilStatement)
TRAVERSAL_CLASS_METHODS(PDTNot)
TRAVERSAL_CLASS_METHODS(PDTNotEqualBool)
TRAVERSAL_CLASS_METHODS(PDTNotEqualNum)
TRAVERSAL_CLASS_METHODS(PDTNumConst)
TRAVERSAL_CLASS_METHODS(PDTNumConstDbl)
TRAVERSAL_CLASS_METHODS(PDTNumConstInt)
TRAVERSAL_CLASS_METHODS(PDTNumVar)
TRAVERSAL_CLASS_METHODS(PDTOr)
TRAVERSAL_CLASS_METHODS(PDTPair)
TRAVERSAL_CLASS_METHODS(PDTPostCondition)
TRAVERSAL_CLASS_METHODS(PDTPreCondition)
TRAVERSAL_CLASS_METHODS(PDTProp)
TRAVERSAL_CLASS_METHODS(PDTRelation)
TRAVERSAL_CLASS_METHODS(PDTSequence)
TRAVERSAL_CLASS_METHODS(PDTSequenceAccessBool)
TRAVERSAL_CLASS_METHODS(PDTSequenceAccessNum)
TRAVERSAL_CLASS_METHODS(PDTSequenceAssign)
TRAVERSAL_CLASS_METHODS(PDTSequenceVar)
TRAVERSAL_CLASS_METHODS(PDTSet)
TRAVERSAL_CLASS_METHODS(PDTSetVar)
TRAVERSAL_CLASS_METHODS(PDTSubt)
TRAVERSAL_CLASS_METHODS(PDTWhile)
TRAVERSAL_CLASS_METHODS(PDTXor)
TRAVERSAL_CLASS_METHODS(PDTUnresolvedExp)
TRAVERSAL_CLASS_METHODS(PDTType)
TRAVERSAL_CLASS_METHODS(PDTUnaryNumExpr)
};

/* 
   The base class B is required to support the following two functions:

    unsigned int getChildrenCountEx() const ;
    PDTNode & getChildEx(unsigned int i);

    It has also to declare a pure virtual function to handle traversal:
    virtual bool traverseEx(Traversal & ) = 0;

    The specialized traversal picks visiting functions of interest
    and implements them for the specific type.
    For example:
        virtual bool pre(PDTA & d) {
        ...
            return true;}

        virtual bool pre(PDTB & d) {
        ...
            return true;}
   */


template<class D, class B>
class TraverseIfc {
    public: 
        virtual unsigned int getChildrenCount() {
            D & me = (D&)(*this);
            return me.getChildrenCountEx();//this should exist in B
        }
        virtual B & getChild(unsigned int i) {
            D & me = (D&)(*this);
            return me.getChildEx(i); // this should exist in B
        };

        // if interested D should implement what is interesting in the 
        // following 4 methods
        virtual Traversal::RetType pre(Traversal& t, D & d) {return Traversal::Continue;}
        virtual Traversal::RetType post(Traversal& t, D & d) {return Traversal::Continue;}
        virtual Traversal::RetType beforeChild(Traversal& t, D & d, unsigned int i) {return Traversal::Continue;}
        virtual Traversal::RetType afterChild(Traversal& t, D & d, unsigned int i) {return Traversal::Continue;}

        // do not touch please!
        virtual Traversal::RetType traverse (Traversal & t) {
            D & me = (D&)(*this);
            Traversal::RetType rc = pre(t,me);
            if (rc == Traversal::Stop)
                return Traversal::Stop;
            if (rc == Traversal::Skip)
                return Traversal::Continue;
            unsigned int i = 0;
            for (; i < getChildrenCount(); i++) {
                B & child = getChild(i);

                rc = beforeChild(t, me, i);
                if (rc == Traversal::Stop)
                    return Traversal::Stop;

                if (rc == Traversal::Skip)
                    continue;

                rc = child.traverseEx(t);
                if (rc == Traversal::Stop)
                    return Traversal::Stop;

                rc = afterChild(t, me, i);
                if (rc == Traversal::Stop)
                    return Traversal::Stop;
            }
            rc = post(t,me);
            if (rc == Traversal::Stop)
                return Traversal::Stop;

            return Traversal::Continue;
        }
};

#define TRAVERSAL_IFC_IMPL(D) \
        virtual Traversal::RetType pre(Traversal& t, D & d) { \
            return t.pre(d);} \
        virtual Traversal::RetType post(Traversal& t, D & d) { \
            return t.post(d);} \
        virtual Traversal::RetType beforeChild(Traversal& t, D & d, unsigned int i) {\
            return t.beforeChild(d,i);} \
        virtual Traversal::RetType afterChild(Traversal& t, D & d, unsigned int i) { \
            return t.afterChild(d,i);} \
        virtual Traversal::RetType traverseEx(Traversal & t) { \
            return TraverseIfc<D,PDTNode>::traverse(t);}

#endif
